/*
 * All rights Reserved, Designed By Rylynn
 * @ClassName:  Measure
 * @Description:
 * @author: rylynn
 * @date:   2021/7/7
 *
 */

package org.etnetwork.core.functions;

import com.google.common.base.Preconditions;
import org.etnetwork.core.AbstractGraph;
import org.etnetwork.core.Edge;
import org.etnetwork.core.Graph;

import java.util.*;

public class Measure {

    public static Double density(AbstractGraph graph) {
        return 0.0d;
    }

    public static Long diameter(AbstractGraph graph) {
        return 0L;
    }

    public static Long radius(AbstractGraph graph) {
        return 0L;
    }

    public static Double distance(AbstractGraph graph, Long sid, Long tid) {
        return shortestPath(graph, false, sid, tid );
    }

    public static Double averageDistance(AbstractGraph graph) {
        return 0.0d;
    }

    public static Double clusterCoefficient(AbstractGraph graph, Long vid) {
        return 0.0d;
    }

    public static Double averageClusterCoefficient(AbstractGraph graph) throws Exception {
        OptionalDouble v = graph.getVertices().stream().mapToDouble(vertex -> clusterCoefficient(graph, vertex.getVid())).average();
        return v.orElseThrow(() -> new Exception(""));

    }


    public static Map<Long, Double> degree(AbstractGraph graph) {
        Map<Long, Double> degreeMap = new HashMap<>();
        for (Edge edge : graph.getEdges()) {
            Long sid = edge.getSource();
            Long tid = edge.getTarget();
            degreeMap.merge(sid, 1.0d, Double::sum);
            degreeMap.merge(tid, 1.0d, Double::sum);
        }
        return degreeMap;
    }

    public static Map<Long, Double> inDegree(AbstractGraph graph) {
        Map<Long, Double> degreeMap = new HashMap<>();
        for (Edge edge : graph.getEdges()) {
            Long sid = edge.getSource();
            Long tid = edge.getTarget();
            degreeMap.merge(sid, 0.0d, Double::sum);
            degreeMap.merge(tid, 1.0d, Double::sum);
        }
        return degreeMap;
    }

    public static Map<Long, Double> outDegree(AbstractGraph graph) {
        Map<Long, Double> degreeMap = new HashMap<>();
        for (Edge edge : graph.getEdges()) {
            Long sid = edge.getSource();
            Long tid = edge.getTarget();
            degreeMap.merge(sid, 1.0d, Double::sum);
            degreeMap.merge(tid, 0.0d, Double::sum);
        }
        return degreeMap;
    }

    public static Map<Long, Double> betweeness(AbstractGraph graph) {
        return new HashMap<>();
    }

    public static Map<Long, Double> pagerank(AbstractGraph graph) {
        return new HashMap<>();
    }

    public static Double shortestPath(AbstractGraph graph, Boolean weighted, Long sid, Long tid) {
        return 0.1d;
    }


    public static Double shortestPath(AbstractGraph graph, Boolean weighted) {
        if (weighted) {
            Boolean checkAllEdgesHaveWeight = graph.getEdges().stream().
                    map(edge -> edge.getProperties().get("weight") == null).
                    reduce((isNull1, isNull2) -> isNull1 & isNull2).orElse(Boolean.FALSE);
            Preconditions.checkArgument(checkAllEdgesHaveWeight, "All edges in graph should have weight in their property map with key \"weight\" when the papameter weighted=true, but missed in this graph");

        }
        return 0.2d;
    }

    public static Double averagePathLength(AbstractGraph graph, Boolean weighted) {
        return 0.1;
    }
}
